package userserv

import (
	"codedef"
	"encoding/json"
	"fmt"
	"github.com/garyburd/redigo/redis"
	"log"
	"net/http"
	"rhelper"
	"strconv"
	"strings"
	"time"
)

type PlanAction struct {
	Cid        string `json:"cid"`
	PlanId     int    `json:"planId"`
	Time       string `json:"time"`
	Repeat     string `json:"repeat"`
	Action     int    `json:"action"`
	Status     string `json:"status"`
	createTime string `json:"createtime"`
}

type devPlanMessage struct {
	Uri         string `json:"uri"`
	Action      string `json:"action"`
	Id          int    `json:"id"`
	Year        int    `json:"year"`
	Month       int    `json:"month"`
	Day         int    `json:"day"`
	StartTime   int    `json:"start_time"`
	StartAction int    `json:"start_action"` //1 ON , 0 OFF
	Pwm         int    `json:"pwm"`
	Loop        uint   `json:"loop"` //bit for loop 0000000x
	Duration    int    `json:"duration"`
	EndAction   int    `json:"end_action"`
	Cd          int    `json:"cd"`
}

func allocPlanId() (int, error) {
	rconn := rhelper.Pool().Get()
	defer rconn.Close()

	id, err := redis.Int(rconn.Do("INCR", rhelper.PlanIdHolder))
	return id, err
}

func createPlan(w http.ResponseWriter, r *http.Request) {
	var uniqueId string
	uniqueId = r.Header.Get("uniqueId")

	log.Printf("createPlan, uniqueId:%s\n", uniqueId)

	message := make([]byte, r.ContentLength)
	n, _ := r.Body.Read(message)
	if n != int(r.ContentLength) {
		log.Println("createPlan failed, can't read request body")
		replyError(w, codedef.ERR_REQUEST_INVALID_PARAM)
		return
	}

	var action PlanAction
	err := json.Unmarshal(message, &action)
	if err != nil {
		log.Println("createPlan failed, json decode failed:", err)
		replyError(w, codedef.ERR_DECODE)
		return
	}

	errCode := checkDevCtrlAction(action.Cid, uniqueId)
	if errCode != codedef.ERR_SUCCEED {
		log.Println("createPlan failed, check task error:", err)
		replyError(w, codedef.ERR_REQUEST_INVALID_PARAM)
		return
	}

	msg := devPlanMessage{}
	msg.Uri = "/timer"
	msg.Action = "add"
	msg.Id, err = allocPlanId()
	if err != nil {
		log.Println("alloc plan id faild", err)
		replyError(w, codedef.ERR_DATABASE)
		return
	}

	if action.Repeat == "" {
		t := time.Now().UTC()
		nowMinutes := converTime2Minute(strconv.Itoa(t.Hour()) + ":" + strconv.Itoa(t.Minute()))
		if nowMinutes > converTime2Minute(action.Time) {
			ts := t.Unix()
			newt := ts + 60*60*24
			nt := time.Unix(newt, 0).UTC()

			msg.Year = nt.Year()
			msg.Month = int(nt.Month())
			msg.Day = nt.Day()
		} else {
			msg.Year = t.Year()
			msg.Month = int(t.Month())
			msg.Day = t.Day()
		}

	} else {
		msg.Year = 2015
		msg.Month = 12
		msg.Day = 1
	}

	msg.StartTime = converTime2Minute(action.Time)
	msg.StartAction = action.Action
	maxPwm_int := GetMaxPwm(w, action.Cid)
	if maxPwm_int == -1 {
		msg.Pwm = 5555 / 3
	}
	msg.Pwm = maxPwm_int / 3
	//msg.Pwm = 50
	msg.Loop = converDays2Bit(action.Repeat)
	msg.Duration = 0
	msg.EndAction = 0
	msg.Cd = 0
	/*type PlanAction struct {
		Cid string `json:"cid"`
		PlanId int `json:"planId"`
		Time string `json:"time"`
		Repeat string `json:"repeat"`
		Action string `json:"action"`
		Status string `json:"status"`
		createTime string `json:"createtime"`
	}*/

	log.Println("message: ", msg)

	action.createTime = strconv.Itoa(int(time.Now().Unix()))

	controller := getOnlineControllerById(action.Cid)
	if controller == nil {
		log.Println("syncPlan2Device failed, no online controller for:", action.Cid)
		replyError(w, codedef.ERR_CONTROLLER_OFFLINE)
		return
	}

	if controller.isBusy() {
		log.Println("syncPlan2Device failed, controller is busy:", action.Cid)
		replyError(w, codedef.ERR_CONTROLLER_BUSY)
		return
	}

	if wait := syn2Device(*controller, msg); wait != nil {
		//log.Println("start to reply app.", string(wait))

		_, errCode := parserDevReply(wait)
		if 0 != errCode {
			replyError(w, codedef.ERR_REQUEST_INVALID_PARAM)
		}

		rconn := rhelper.Pool().Get()
		defer rconn.Close()
		rconn.Send("MULTI")
		rconn.Send("RPUSH", rhelper.PlanSort+action.Cid, strconv.Itoa(msg.Id))
		rconn.Send("HMSET", rhelper.PlanHash+action.Cid+":"+strconv.Itoa(msg.Id), "planid", msg.Id, "time",
			action.Time, "repeat", action.Repeat, "action", action.Action, "status", action.Status, "createtime", action.createTime, "createBy", uniqueId)
		_, err := rconn.Do("EXEC")
		if err != nil {
			log.Println("save plan failed, error:", err)
			replyError(w, codedef.ERR_DATABASE)
			return
		}

		replyBuffer(w, []byte(fmt.Sprintf("{\"planId\":%d}", msg.Id)))
	} else {
		log.Println("save setSwitch failed, error:", err)
		replyError(w, codedef.ERR_CONTROLLER_NO_RESPONSE)
		return
	}
}

func converTime2Minute(time string) (minutes int) {
	if time == "" {
		minutes = -1
		return
	}

	var result []string
	if strings.Contains(time, ":") {
		result = strings.Split(time, ":")
		//minute = strconv.Atoi(result[0]) * 60 + strconv.Atoi(result[1])
		h, err := strconv.Atoi(result[0])
		if err != nil {
			minutes = -1
			return
		}
		m, err := strconv.Atoi(result[1])
		if err != nil {
			minutes = -1
			return
		}

		minutes = h*60 + m
	} else {
		minutes = -1
		return
	}

	return
}

func converDays2Bit(repeatStr string) uint {
	weekdays := rhelper.SplitString2Array(repeatStr, ",")
	var result uint = 0
	for _, day := range weekdays {
		daynum, _ := strconv.Atoi(day)
		offset := uint(daynum)
		result = result | (1 << offset)
	}
	//result = result << 1
	return result
	//repeatBit = 0
	//
	//var result []string
	//var binary uint
	//if strings.Contains(repeatStr, ",") {
	//	result = strings.Split(repeatStr, ",")
	//	for _, n := range result{
	//		offset, err := strconv.Atoi(n)
	//		if nil != err{
	//			repeatBit = 0
	//			return
	//		}
	//		uoffset := uint(offset-1)
	//		binary = 1 << uoffset
	//		repeatBit = repeatBit | binary
	//	}
	//} else {
	//	offset, err := strconv.Atoi(repeatStr)
	//	if nil != err{
	//		repeatBit = 0
	//		return
	//	}
	//	uoffset := uint(offset-1)
	//	binary = 1 << uoffset
	//	repeatBit = 1 << uoffset
	//}
	//return
}

func selectPlan(w http.ResponseWriter, r *http.Request) {
	type selectPlan struct {
		Cid string `json:"cid"`
	}

	type replyPlanList struct {
		Plan []PlanAction `json:"plan"`
	}

	var uniqueId string
	uniqueId = r.Header.Get("uniqueId")

	log.Printf("selectPlan, uniqueId:%s\n", uniqueId)

	message := make([]byte, r.ContentLength)
	n, _ := r.Body.Read(message)
	if n != int(r.ContentLength) {
		log.Println("selectPlan failed, can't read request body")
		replyError(w, codedef.ERR_REQUEST_INVALID_PARAM)
		return
	}

	var action selectPlan
	err := json.Unmarshal(message, &action)
	if err != nil {
		log.Println("selectPlan failed, json decode failed:", err)
		replyError(w, codedef.ERR_DECODE)
		return
	}

	errCode := checkDevCtrlAction(action.Cid, uniqueId)
	if errCode != codedef.ERR_SUCCEED {
		log.Println("selectPlan failed, check task error:", err)
		replyError(w, codedef.ERR_REQUEST_INVALID_PARAM)
		return
	}

	rconn := rhelper.Pool().Get()
	defer rconn.Close()

	Plans, err := redis.Strings(rconn.Do("LRANGE", rhelper.PlanSort+action.Cid, "0", "-1"))
	log.Println("LRANGE", rhelper.PlanHash+action.Cid, "0", "-1")
	if nil != err {
		log.Println("selectPlan failed, redis failed:", err)
		replyError(w, codedef.ERR_DATABASE)
		return
	}

	rpl := replyPlanList{}
	pl := PlanAction{}

	for _, plan := range Plans {
		planArrays, err := redis.Strings(rconn.Do("HMGET", rhelper.PlanHash+action.Cid+":"+plan, "planid", "time", "repeat", "action", "status"))
		//log.Println("HMGET", plan, "planid", "time", "repeat", "action", "status")
		if nil != err {
			log.Println("selectPlan failed, redis failed:", err)
			return
		}
		pl.Cid = action.Cid
		pl.PlanId, _ = strconv.Atoi(planArrays[0])
		pl.Time = planArrays[1]
		pl.Repeat = planArrays[2]
		pl.Action, _ = strconv.Atoi(planArrays[3])
		pl.Status = planArrays[4]

		rpl.Plan = append(rpl.Plan, pl)
	}

	replyJSON(w, rpl)

}

func deletePlan(w http.ResponseWriter, r *http.Request) {
	type selectPlan struct {
		Cid    string `json:"cid"`
		PlanId int    `json:"planId"`
	}

	var uniqueId string
	uniqueId = r.Header.Get("uniqueId")

	log.Printf("deletePlan, uniqueId:%s\n", uniqueId)

	message := make([]byte, r.ContentLength)
	n, _ := r.Body.Read(message)
	if n != int(r.ContentLength) {
		log.Println("deletePlan failed, can't read request body")
		replyError(w, codedef.ERR_REQUEST_INVALID_PARAM)
		return
	}

	var action selectPlan
	err := json.Unmarshal(message, &action)
	if err != nil {
		log.Println("deletePlan failed, json decode failed:", err)
		replyError(w, codedef.ERR_DECODE)
		return
	}

	errCode := checkDevCtrlAction(action.Cid, uniqueId)
	if errCode != codedef.ERR_SUCCEED {
		log.Println("deletePlan failed, check task error:", err)
		replyError(w, codedef.ERR_REQUEST_INVALID_PARAM)
		return
	}

	msg := devPlanMessage{}
	msg.Uri = "/timer"
	msg.Action = "del"
	msg.Id = action.PlanId

	log.Println("message: ", msg)

	controller := getOnlineControllerById(action.Cid)
	if controller == nil {
		log.Println("syncPlan2Device failed, no online controller for:", action.Cid)
		replyError(w, codedef.ERR_CONTROLLER_OFFLINE)
		return
	}

	if controller.isBusy() {
		log.Println("syncPlan2Device failed, controller is busy:", action.Cid)
		replyError(w, codedef.ERR_CONTROLLER_BUSY)
		return
	}

	if wait := syn2Device(*controller, msg); wait != nil {
		//log.Println("start to reply app.", string(wait))

		devMessage, errCode := parserDevReply(wait)
		if 0 != errCode {
			replyError(w, codedef.ERR_REQUEST_INVALID_PARAM)
		}

		rconn := rhelper.Pool().Get()
		defer rconn.Close()

		rconn.Send("MULTI")
		rconn.Send("DEL", rhelper.PlanHash+action.Cid+":"+strconv.Itoa(action.PlanId))
		rconn.Send("LREM", rhelper.PlanSort+action.Cid, "0", strconv.Itoa(action.PlanId))
		_, err := rconn.Do("EXEC")
		if err != nil {
			log.Println("save plan failed, error:", err)
			replyError(w, codedef.ERR_DATABASE)
			return
		}

		replyBuffer(w, devMessage)
	} else {
		log.Println("save setSwitch failed, error:", err)
		replyError(w, codedef.ERR_CONTROLLER_NO_RESPONSE)
		return
	}
}

func updatePlan(w http.ResponseWriter, r *http.Request) {
	var uniqueId string
	uniqueId = r.Header.Get("uniqueId")

	log.Printf("updatePlan, uniqueId:%s\n", uniqueId)

	message := make([]byte, r.ContentLength)
	n, _ := r.Body.Read(message)
	if n != int(r.ContentLength) {
		log.Println("createPlan failed, can't read request body")
		replyError(w, codedef.ERR_REQUEST_INVALID_PARAM)
		return
	}

	var action PlanAction
	err := json.Unmarshal(message, &action)
	if err != nil {
		log.Println("createPlan failed, json decode failed:", err)
		replyError(w, codedef.ERR_DECODE)
		return
	}

	errCode := checkDevCtrlAction(action.Cid, uniqueId)
	if errCode != codedef.ERR_SUCCEED {
		log.Println("createPlan failed, check task error:", err)
		replyError(w, codedef.ERR_REQUEST_INVALID_PARAM)
		return
	}

	if action.PlanId == 0 {
		log.Println("update plan faild, id nil", err)
		replyError(w, codedef.ERR_DECODE)
		return
	}

	msg := devPlanMessage{}
	msg.Uri = "/timer"

	msg.Action = "update"

	msg.Id = action.PlanId

	if action.Repeat == "" {
		t := time.Now().UTC()
		nowMinutes := converTime2Minute(strconv.Itoa(t.Hour()) + ":" + strconv.Itoa(t.Minute()))
		if nowMinutes > converTime2Minute(action.Time) {
			ts := t.Unix()
			newt := ts + 60*60*24
			nt := time.Unix(newt, 0).UTC()

			msg.Year = nt.Year()
			msg.Month = int(nt.Month())
			msg.Day = nt.Day()
		} else {
			msg.Year = t.Year()
			msg.Month = int(t.Month())
			msg.Day = t.Day()
		}

	} else {
		msg.Year = 1990
		msg.Month = 12
		msg.Day = 1
	}

	msg.StartTime = converTime2Minute(action.Time)
	msg.StartAction = action.Action
	msg.Pwm = 50
	msg.Loop = converDays2Bit(action.Repeat)
	msg.Duration = 0
	msg.EndAction = 0
	msg.Cd = 0

	log.Println("message: ", msg)

	controller := getOnlineControllerById(action.Cid)
	if controller == nil {
		log.Println("syncPlan2Device failed, no online controller for:", action.Cid)
		replyError(w, codedef.ERR_CONTROLLER_OFFLINE)
		return
	}

	if controller.isBusy() {
		log.Println("syncPlan2Device failed, controller is busy:", action.Cid)
		replyError(w, codedef.ERR_CONTROLLER_BUSY)
		return
	}

	if wait := syn2Device(*controller, msg); wait != nil {
		//log.Println("start to reply app.", string(wait))

		devMessage, errCode := parserDevReply(wait)
		if 0 != errCode {
			replyError(w, codedef.ERR_REQUEST_INVALID_PARAM)
		}

		rconn := rhelper.Pool().Get()
		defer rconn.Close()
		rconn.Send("MULTI")
		rconn.Send("HMSET", rhelper.PlanHash+action.Cid+":"+strconv.Itoa(msg.Id), "planid", msg.Id, "time",
			action.Time, "repeat", action.Repeat, "action", action.Action, "status", action.Status, "createtime", action.createTime)
		_, err := rconn.Do("EXEC")
		if err != nil {
			log.Println("save plan failed, error:", err)
			replyError(w, codedef.ERR_DATABASE)
			return
		}

		replyBuffer(w, devMessage)
	} else {
		log.Println("save setSwitch failed, error:", err)
		replyError(w, codedef.ERR_CONTROLLER_NO_RESPONSE)
		return
	}
}

func setStatusPlan(w http.ResponseWriter, r *http.Request) {
	var uniqueId string
	uniqueId = r.Header.Get("uniqueId")

	log.Printf("updatePlan, uniqueId:%s\n", uniqueId)

	message := make([]byte, r.ContentLength)
	n, _ := r.Body.Read(message)
	if n != int(r.ContentLength) {
		log.Println("createPlan failed, can't read request body")
		replyError(w, codedef.ERR_REQUEST_INVALID_PARAM)
		return
	}

	var action PlanAction
	err := json.Unmarshal(message, &action)
	if err != nil {
		log.Println("createPlan failed, json decode failed:", err)
		replyError(w, codedef.ERR_DECODE)
		return
	}

	errCode := checkDevCtrlAction(action.Cid, uniqueId)
	if errCode != codedef.ERR_SUCCEED {
		log.Println("createPlan failed, check task error:", err)
		replyError(w, codedef.ERR_REQUEST_INVALID_PARAM)
		return
	}

	if action.PlanId == 0 {
		log.Println("update plan faild, id nil", err)
		replyError(w, codedef.ERR_DECODE)
		return
	}

	msg := devPlanMessage{}
	msg.Uri = "/timer"

	if action.Status == "0" {
		msg.Action = "delete"
		msg.Id = action.PlanId
	} else {
		msg.Action = "add"
		msg.Id = action.PlanId

		if action.Repeat == "" {
			t := time.Now().UTC()
			nowMinutes := converTime2Minute(strconv.Itoa(t.Hour()) + ":" + strconv.Itoa(t.Minute()))
			if nowMinutes > converTime2Minute(action.Time) {
				ts := t.Unix()
				newt := ts + 60*60*24
				nt := time.Unix(newt, 0).UTC()

				msg.Year = nt.Year()
				msg.Month = int(nt.Month())
				msg.Day = nt.Day()
			} else {
				msg.Year = t.Year()
				msg.Month = int(t.Month())
				msg.Day = t.Day()
			}

		} else {
			msg.Year = 1990
			msg.Month = 12
			msg.Day = 1
		}

		msg.StartTime = converTime2Minute(action.Time)
		msg.StartAction = action.Action
		msg.Pwm = 50
		msg.Loop = converDays2Bit(action.Repeat)
		msg.Duration = 0
		msg.EndAction = 0
		msg.Cd = 0
	}

	log.Println("message: ", msg)

	controller := getOnlineControllerById(action.Cid)
	if controller == nil {
		log.Println("syncPlan2Device failed, no online controller for:", action.Cid)
		replyError(w, codedef.ERR_CONTROLLER_OFFLINE)
		return
	}

	if controller.isBusy() {
		log.Println("syncPlan2Device failed, controller is busy:", action.Cid)
		replyError(w, codedef.ERR_CONTROLLER_BUSY)
		return
	}

	if wait := syn2Device(*controller, msg); wait != nil {
		//log.Println("start to reply app.", string(wait))

		devMessage, errCode := parserDevReply(wait)
		if 0 != errCode {
			replyError(w, codedef.ERR_REQUEST_INVALID_PARAM)
		}

		rconn := rhelper.Pool().Get()
		defer rconn.Close()
		rconn.Send("MULTI")
		rconn.Send("HMSET", rhelper.PlanHash+action.Cid+":"+strconv.Itoa(msg.Id), "planid", msg.Id, "time",
			action.Time, "repeat", action.Repeat, "action", action.Action, "status", action.Status, "createtime", action.createTime)
		_, err := rconn.Do("EXEC")
		if err != nil {
			log.Println("save plan failed, error:", err)
			replyError(w, codedef.ERR_DATABASE)
			return
		}

		replyBuffer(w, devMessage)
	} else {
		log.Println("save setSwitch failed, error:", err)
		replyError(w, codedef.ERR_CONTROLLER_NO_RESPONSE)
		return
	}
}

func init() {
	myMux.muxHandlers.HandleFunc("/createPlan", createPlan)
	myMux.muxHandlers.HandleFunc("/selectPlan", selectPlan)
	myMux.muxHandlers.HandleFunc("/deletePlan", deletePlan)
	myMux.muxHandlers.HandleFunc("/updatePlan", updatePlan)
	myMux.muxHandlers.HandleFunc("/setStatusPlan", setStatusPlan)
}
